﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using dIRca.Data;
using dIRca.Data.Services;
using dIRca.Extras.Services;
using dIRca.Network;
using GalaSoft.MvvmLight.Command;
using WP7Contrib.Collections;
using WP7Contrib.Logging;
using WP7Contrib.Services.Navigation;
using WP7Contrib.Services.Storage;

namespace dIRca.WP7.ViewModels
{
    public class MainViewModel : ViewModelBaseWp7
    {
        #region Commands
        RelayCommand<SelectionChangedEventArgs> _selectionChangedCommand;
        public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand
        {
            get
            {
                return _selectionChangedCommand ?? (_selectionChangedCommand =
                    new RelayCommand<SelectionChangedEventArgs>(items => SelectionChanged(items)));
            }
        }

        private bool _canConnect;
        public bool CanConnect
        {
            get { return _canConnect; }
            set
            {
                if (_canConnect == value)
                    return;

                var oldValue = _canConnect;
                _canConnect = value;
                ConnectCommand.RaiseCanExecuteChanged();
                RaisePropertyChanged("CanConnect", oldValue, CanConnect, true);
            }
        }

        RelayCommand _connectCommand;
        public RelayCommand ConnectCommand
        {
            get
            {
                return _connectCommand ?? (_connectCommand =
                    new RelayCommand(() => this.Connect(), () => this.CanConnect));
            }
        }

        private bool _canDisconnect;
        public bool CanDisconnect
        {
            get { return _canDisconnect; }
            set
            {
                if (_canDisconnect == value)
                    return;

                var oldValue = _canDisconnect;
                _canDisconnect = value;
                DisconnectCommand.RaiseCanExecuteChanged();
                RaisePropertyChanged("CanDisconnect", oldValue, CanDisconnect, true);
            }
        }

        RelayCommand _disconnectCommand;
        public RelayCommand DisconnectCommand
        {
            get
            {
                return _disconnectCommand ?? (_disconnectCommand =
                    new RelayCommand(() => this.Disconnect(), () => this.CanDisconnect));
            }
        }

        RelayCommand _connectionsListCommand;
        public RelayCommand ConnectionsListCommand
        {
            get
            {
                return _connectionsListCommand ?? (_connectionsListCommand =
                    new RelayCommand(() => this.ConnectionsList()));
            }
        }

        RelayCommand _settingsCommand;
        public RelayCommand SettingsCommand
        {
            get
            {
                return _settingsCommand ?? (_settingsCommand =
                    new RelayCommand(() => this.Settings()));
            }
        }

        RelayCommand _aboutCommand;
        public RelayCommand AboutCommand
        {
            get
            {
                return _aboutCommand ?? (_aboutCommand =
                    new RelayCommand(() => this.About()));
            }
        }

        RelayCommand _screenshotCommand;
        public RelayCommand ScreenshotCommand
        {
            get
            {
                return _screenshotCommand ?? (_screenshotCommand =
                    new RelayCommand(() => this.TakeScreenshot()));
            }
        }

        RelayCommand _sendCommand;
        public RelayCommand SendCommand
        {
            get
            {
                return _sendCommand ?? (_sendCommand =
                    new RelayCommand(() => this.Send()));
            }
        }

        private bool _canJoin;
        public bool CanJoin
        {
            get { return _canJoin; }
            set
            {
                if (_canJoin == value)
                    return;

                var oldValue = _canJoin;
                _canJoin = value;
                JoinCommand.RaiseCanExecuteChanged();
                RaisePropertyChanged("CanJoin", oldValue, CanJoin, true);
            }
        }

        RelayCommand _joinCommand;
        public RelayCommand JoinCommand
        {
            get
            {
                return _joinCommand ?? (_joinCommand =
                    new RelayCommand(() => this.Join(), () => this.CanJoin));
            }
        }

        RelayCommand<FrameworkElement> _layoutRootCommand;
        public RelayCommand<FrameworkElement> LayoutRootCommand
        {
            get
            {
                return _layoutRootCommand ?? (_layoutRootCommand =
                    new RelayCommand<FrameworkElement>(sender => this.LayoutRoot = sender));
            }
        }
        #endregion

        #region Properties
        private readonly ILastConnectionService _lastConnectionService;
        private readonly ISettingService _settingService;

        private string _pageTitle;
        public string PageTitle
        {
            get { return _pageTitle; }
            set
            {
                if (_pageTitle == value)
                    return;

                var oldValue = _pageTitle;
                _pageTitle = value;
                RaisePropertyChanged("PageTitle", oldValue, PageTitle, true);
            }
        }

        private ObservableCollection<ChannelViewModel> _pages;
        public ObservableCollection<ChannelViewModel> Pages
        {
            get { return _pages; }
            set
            {
                if (_pages == value)
                    return;

                var oldValue = _pages;
                _pages = value;
                RaisePropertyChanged("Pages", oldValue, Pages, true);
            }
        }

        private ChannelViewModel _selectedPage;
        public ChannelViewModel SelectedPage
        {
            get { return _selectedPage; }
            set
            {
                if (_selectedPage == value)
                    return;

                var oldValue = _selectedPage;
                _selectedPage = value;
                RaisePropertyChanged("SelectedPage", oldValue, SelectedPage, true);
            }
        }

        private string _sendBuffer;
        public string SendBuffer
        {
            get { return _sendBuffer; }
            set
            {
                if (_sendBuffer == value)
                    return;

                var oldValue = _sendBuffer;
                _sendBuffer = value;
                RaisePropertyChanged("SendBuffer", oldValue, SendBuffer, true);
            }
        }

        private ObservableCollection<string> _commandHistory;
        public ObservableCollection<string> CommandHistory
        {
            get { return _commandHistory; }
            set
            {
                if (_commandHistory == value)
                    return;

                var oldValue = _commandHistory;
                _commandHistory = value;
                RaisePropertyChanged("CommandHistory", oldValue, CommandHistory, true);
            }
        }

        public FrameworkElement LayoutRoot { get; private set; }

        public ConnectionManager _connectionManager = null;

        private bool _isAutoScrollOnOutput;
        #endregion

        public MainViewModel(INavigationService navigationService, IStorageService storageService, ILog log, ISettingService settingService, ILastConnectionService lastConnectionService)
            : base(navigationService, storageService, log)
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
                _connectionManager = new ConnectionManager();
            }
            else
            {
                // Code runs "for real"
                _settingService = settingService;
                _lastConnectionService = lastConnectionService;
                _connectionManager = new ConnectionManager(_settingService.Settings);
            }
            _isAutoScrollOnOutput = _connectionManager.IsAutoScrollOnOutput;
            CanConnect = true;
            SendBuffer = "JOIN ";
            PageTitle = App.Title + " [NickName]->[server]";

            Pages = new ObservableCollection<ChannelViewModel>();
            var status = new ChannelViewModel(_connectionManager.StatusLog);
            Pages.Add(status);
            SelectedPage = status;
            CommandHistory = new ObservableCollection<string>();
            _connectionManager.ChannelAdded += new EventHandler<RecipientEventArgs>(_connectionManager_ChannelAdded);
            _connectionManager.ChannelRemoved += new EventHandler<RecipientEventArgs>(_connectionManager_ChannelRemoved);
            _connectionManager.RefreshMessages += new EventHandler<RefreshMessagesEventArgs>(_connectionManager_RefreshMessages);
        }

        void SelectionChanged(SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 0)
                return;

            SelectedPage = e.AddedItems[0] as ChannelViewModel;
        }

        //void OnMessageReceived(object sender, MessageEventArgs e)
        //{
        //    if (!string.IsNullOrEmpty(e.Message.ServerAddress))
        //    {
        //        // HACK: Works on Unreal IRCD, the first thing that sends you a notice is the server
        //        if (e.Message.ReceiveType == ReceiveType.QueryNotice)
        //        {
        //            if (_isFirstMessage)
        //            {
        //                _isFirstMessage = false;
        //                ExecuteOnUIThread(() =>
        //                {
        //                    PageTitle = "dIRca [NickName]->[" + e.Message.ServerAddress + "]";
        //                });
        //            }
        //        }
        //        // EFNet likely has to be built from links + lookup (Links command only shows a description)
        //    }
        //}

        void _connectionManager_ChannelAdded(object sender, RecipientEventArgs e)
        {
            ExecuteOnUIThread(() =>
            {
                var recipient = new ChannelViewModel(e.Recipient);
                Pages.Add(recipient);
                SelectedPage = recipient;
            });
        }

        void _connectionManager_ChannelRemoved(object sender, RecipientEventArgs e)
        {
            ExecuteOnUIThread(() =>
            {
                var intendedPage = (from p in Pages
                                    where p.Model.Name == e.Recipient.Name
                                    select p).FirstOrDefault();
                if (Pages.Contains(intendedPage))
                    Pages.Remove(intendedPage);
                SelectedPage = Pages[0];
            });
        }

        void _connectionManager_RefreshMessages(object sender, RefreshMessagesEventArgs e)
        {
            if (!CanConnect)
                CanConnect = true;
            // Any time a message is sent across the wire, a disconnect is possible
            if (!CanDisconnect)
                CanDisconnect = true;
            ExecuteOnUIThread(() =>
            {
                var intendedPage = (from p in Pages
                                    where p.Model.Name == e.Name
                                    select p).FirstOrDefault();
                intendedPage.Refresh(_isAutoScrollOnOutput);
            });
        }

        void Connect()
        {
            CanConnect = false;
            _connectionManager.Connect();
        }

        void Disconnect()
        {
            CanDisconnect = false;
            _connectionManager.Disconnect();
            _lastConnectionService.Add(_connectionManager.LastConnection);
        }

        public void ConnectionsList()
        {
            NavigationService.Navigate(new Uri("/Views/ConnectionsView.xaml", UriKind.Relative));
        }

        public void Settings()
        {
            NavigationService.Navigate(new Uri("/Views/SettingsView.xaml", UriKind.Relative));
        }

        public void About()
        {
            NavigationService.Navigate(new Uri("/Views/AboutView.xaml", UriKind.Relative));
        }

        void Send()
        {
            if (!string.IsNullOrEmpty(SendBuffer))
                if (SelectedPage != null)
                {
                    CommandHistory.Add(SendBuffer);
                    _connectionManager.SendMessage(SelectedPage.Model.Name, SendBuffer);
                    SendBuffer = string.Empty;
                }
        }

        void TakeScreenshot()
        {
            var service = new Screenshots(LayoutRoot, App.Title);
            service.Save();
        }

        void Join()
        {

        }

        protected override void IsBeingActivated(IStorage storage)
        {
        }

        protected override void IsBeingDeactivated(IStorage storage)
        {
        }
    }
}